#include "U2DynLib.h"

#include "U2Exception.h"
#include "U2LogManager.h"


#if U2_PLATFORM == U2_PLATFORM_WIN32
#   define WIN32_LEAN_AND_MEAN
#   if !defined(NOMINMAX) && defined(_MSC_VER)
#       define NOMINMAX // required to stop windows.h messing up std::min
#   endif
#   include <windows.h>
#endif

#if U2_PLATFORM == U2_PLATFORM_APPLE || U2_PLATFORM == U2_PLATFORM_APPLE_IOS
#   include "macUtils.h"
#   include <dlfcn.h>
#endif


U2EG_NAMESPACE_USING


//-----------------------------------------------------------------------
U2DynLib::U2DynLib( const U2String& name )
{
    mName = name;
    mInst = NULL;
}

//-----------------------------------------------------------------------
U2DynLib::~U2DynLib()
{
}

//-----------------------------------------------------------------------
void U2DynLib::load()
{
    // Log library load
    U2LogManager::getSingleton().logMessage("Loading library " + mName);

	U2String name = mName;
#if U2_PLATFORM == U2_PLATFORM_LINUX || U2_PLATFORM == U2_PLATFORM_TEGRA2
    // dlopen() does not add .so to the filename, like windows does for .dll
    if (name.find(".so") == U2String::npos)
        name += ".so";
#elif U2_PLATFORM == U2_PLATFORM_APPLE
    // dlopen() does not add .dylib to the filename, like windows does for .dll
    if (name.substr(name.length() - 6, 6) != ".dylib")
		name += ".dylib";
#elif U2_PLATFORM == U2_PLATFORM_WIN32
	// Although LoadLibraryEx will add .dll itself when you only specify the library name,
	// if you include a relative path then it does not. So, add it to be sure.
	if (name.substr(name.length() - 4, 4) != ".dll")
		name += ".dll";
#endif
    mInst = (DYNLIB_HANDLE)DYNLIB_LOAD( name.c_str() );

    if( !mInst )
    {
        U2_EXCEPT(
            U2Exception::ERR_INTERNAL_ERROR, 
            "Could not load dynamic library " + mName + 
            ".  System Error: " + dynlibError(),
            "U2DynLib::load" );
    }
}

//-----------------------------------------------------------------------
void U2DynLib::unload()
{
    // Log library unload
    U2LogManager::getSingleton().logMessage("Unloading library " + mName);

    if( DYNLIB_UNLOAD( mInst ) )
	{
        U2_EXCEPT(
            U2Exception::ERR_INTERNAL_ERROR, 
            "Could not unload dynamic library " + mName +
            ".  System Error: " + dynlibError(),
            "U2DynLib::unload");
	}

}

//-----------------------------------------------------------------------
void* U2DynLib::getSymbol( const U2String& strName ) const throw()
{
    return (void*)DYNLIB_GETSYM( mInst, strName.c_str() );
}
//-----------------------------------------------------------------------
U2String U2DynLib::dynlibError( void ) 
{
#if U2_PLATFORM == U2_PLATFORM_WIN32
    LPVOID lpMsgBuf; 
    FormatMessage( 
        FORMAT_MESSAGE_ALLOCATE_BUFFER | 
        FORMAT_MESSAGE_FROM_SYSTEM | 
        FORMAT_MESSAGE_IGNORE_INSERTS, 
        NULL, 
        GetLastError(), 
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 
        (LPTSTR) &lpMsgBuf, 
        0, 
        NULL 
        ); 
    U2String ret = (char*)lpMsgBuf;
    // Free the buffer.
    LocalFree( lpMsgBuf );
    return ret;
#elif U2_PLATFORM == U2_PLATFORM_LINUX || U2_PLATFORM == U2_PLATFORM_APPLE || U2_PLATFORM == U2_PLATFORM_TEGRA2
    return U2String(dlerror());
#else
    return U2String("");
#endif
}


